/*!
  \file gv_quick.c
 
  \brief OGSF library - 
 
  GRASS OpenGL gsurf OGSF Library 

  Trying some stuff to draw a quick version of a vector map, to represent
  it when doing interactive translations.

  (C) 1999-2008 by the GRASS Development Team
 
  This program is free software under the 
  GNU General Public License (>=v2). 
  Read the file COPYING that comes with GRASS
  for details.

  \author Bill Brown, USACERL (December 1993)
  \author Doxygenized by Martin Landa <landa.martin gmail.com> (May 2008)
*/

#include <stdio.h>
#include <stdlib.h>

#include <grass/gis.h>
#include <grass/gstypes.h>

#include "rowcol.h"

/*!
  \brief target number of desired points to represent entire file
*/
#define TFAST_PTS 800

/*!
  \brief max number of lines desired
*/
#define MFAST_LNS  400

static geoline *copy_line(geoline *);
static geoline *thin_line(geoline *, float);

/*!
  \brief Copy line

  \param gln source line (geoline)

  \return pointer to geoline struct
  \return on failure
*/
static geoline *copy_line(geoline * gln)
{
    geoline *newln;
    int i, np;

    newln = (geoline *) G_malloc(sizeof(geoline)); /* G_fatal_error */
    if (!newln) {
	return (NULL);
    }

    np = newln->npts = gln->npts;

    if (2 == (newln->dims = gln->dims)) {
	newln->p2 = (Point2 *) G_calloc(np, sizeof(Point2)); /* G_fatal_error */
	if (!newln->p2) {
	    return (NULL);
	}

	for (i = 0; i < np; i++) {
	    newln->p2[i][X] = gln->p2[i][X];
	    newln->p2[i][Y] = gln->p2[i][Y];
	}
    }
    else {
	newln->p3 = (Point3 *) G_calloc(np, sizeof(Point3)); /* G_fatal_error */
	if (!newln->p3) {
	    return (NULL);
	}

	for (i = 0; i < np; i++) {
	    newln->p3[i][X] = gln->p3[i][X];
	    newln->p3[i][Y] = gln->p3[i][Y];
	    newln->p3[i][Z] = gln->p3[i][Z];
	}
    }

    newln->next = NULL;

    return (newln);
}


/*!
  \brief Thin line

  For now, just eliminate points at regular interval

  \param gln line (geoline)
  \param factor

  \return pointer to geoline struct
  \return NULL on failure
*/
static geoline *thin_line(geoline * gln, float factor)
{
    geoline *newln;
    int i, nextp, targp;

    newln = (geoline *) G_malloc(sizeof(geoline)); /* G_fatal_error */
    if (!newln) {
	return (NULL);
    }

    targp = (int) (gln->npts / factor);

    if (targp < 2) {
	targp = 2;
    }

    newln->npts = targp;

    if (2 == (newln->dims = gln->dims)) {
	newln->p2 = (Point2 *) G_calloc(targp, sizeof(Point2)); /* G_fatal_error */
	if (!newln->p2) {
	    return (NULL);
	}

	for (i = 0; i < targp; i++) {
	    if (i == targp - 1) {
		nextp = gln->npts - 1;	/* avoid rounding error */
	    }
	    else {
		nextp = (int) ((i * (gln->npts - 1)) / (targp - 1));
	    }

	    newln->p2[i][X] = gln->p2[nextp][X];
	    newln->p2[i][Y] = gln->p2[nextp][Y];
	}
    }
    else {
	newln->p3 = (Point3 *) G_calloc(targp, sizeof(Point3)); /* G_fatal_error */
	if (!newln->p3) {
	    return (NULL);
	}

	for (i = 0; i < targp; i++) {
	    if (i == targp - 1) {
		nextp = gln->npts - 1;	/* avoid rounding error */
	    }
	    else {
		nextp = (int) ((i * (gln->npts - 1)) / (targp - 1));
	    }

	    newln->p3[i][X] = gln->p3[nextp][X];
	    newln->p3[i][Y] = gln->p3[nextp][Y];
	    newln->p3[i][Z] = gln->p3[nextp][Z];
	}
    }

    newln->next = NULL;

    return (newln);
}

/*!
  \brief Get line width

  \param gln line (geoline)

  \return line width
*/
float gv_line_length(geoline * gln)
{
    int n;
    float length = 0.0;

    for (n = 0; n < gln->npts - 1; n++) {
	if (gln->p2) {
	    length += GS_P2distance(gln->p2[n + 1], gln->p2[n]);
	}
	else {
	    length += GS_distance(gln->p3[n + 1], gln->p3[n]);
	}
    }

    return (length);
}

/*!
  \brief Get number of line vertices

  \param gln line (geoline)

  \return number of vertices
*/
int gln_num_points(geoline * gln)
{
    int np = 0;
    geoline *tln;

    for (tln = gln; tln; tln = tln->next) {
	np += tln->npts;
    }

    return (np);
}

/*!
  \brief Get number of points in vector

  \param gv vector (geovect)

  \return number of points
*/
int gv_num_points(geovect * gv)
{
    return (gln_num_points(gv->lines));
}



/*!
  \brief Decimate line

  strategy here: if line has more than average number of points, decimate
  by eliminating points, otherwise decimate by eliminating shorter lines

  \param gv vector (geovect)

  \return
*/
int gv_decimate_lines(geovect * gv)
{
    int T_pts, A_ppl, N_s;
    float decim_factor, slength[MFAST_LNS], T_slength, A_slength;
    geoline *gln, *prev;

    /* should check if already exists & free if != gv->lines */
    if (TFAST_PTS > (T_pts = gv_num_points(gv))) {
	gv->fastlines = gv->lines;

	return (1);
    }

    N_s = 0;
    T_slength = 0.0;
    decim_factor = T_pts / TFAST_PTS;
    A_ppl = T_pts / gv->n_lines;	/* (int) Average points per line */

    prev = NULL;

    for (gln = gv->lines; gln; gln = gln->next) {
	if (gln->npts > A_ppl) {
	    if (prev) {
		prev->next = thin_line(gln, decim_factor);
		prev = prev->next;
	    }
	    else {
		prev = gv->fastlines = thin_line(gln, decim_factor);
	    }
	}
	else if (N_s < MFAST_LNS) {
	    T_slength += slength[N_s++] = gv_line_length(gln);
	}
    }

    A_slength = T_slength / N_s;
    N_s = 0;

    for (gln = gv->lines; gln; gln = gln->next) {
	if (gln->npts <= A_ppl) {
	    if (N_s < MFAST_LNS) {
		if (slength[N_s++] > A_slength) {
		    if (prev) {
			prev->next = copy_line(gln);
			prev = prev->next;
		    }
		    else {
			prev = gv->fastlines = copy_line(gln);
		    }
		}
	    }
	}
    }

    G_debug (3, "Decimated lines have %d points.",
	     gln_num_points(gv->fastlines));

    return (1);
}
